package com.lxg.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lxg.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 陆小根
 * date: 2022/04/24
 * Description:
 */

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  private MyUserDetailsService myUserDetailsService;

  @Autowired
  public SecurityConfig(MyUserDetailsService myUserDetailsService) {
    this.myUserDetailsService = myUserDetailsService;
  }

  //  @Bean
//  public UserDetailsService userDetailsService() {
//    // 先用内存中的
//    InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//    inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
//    return inMemoryUserDetailsManager;
//  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(myUserDetailsService);
  }

  @Override
  // 暴露我们的AuthenticationManager
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  // 自定义的filter交给工厂管理
  @Bean
  public LoginFilter loginFilter() throws Exception {
    LoginFilter loginFilter = new LoginFilter();
    loginFilter.setFilterProcessesUrl("/doLogin"); // 指定认证的url
    loginFilter.setUsernameParameter("uname"); // 指定接收json 用户名 key
    loginFilter.setPasswordParameter("passwd"); // 指定接口json 密码 key
    loginFilter.setAuthenticationManager(authenticationManagerBean());
    loginFilter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
      @Override
      public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "登录成功");
        result.put("用户信息", authentication.getPrincipal());
        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
      }
    }); // 认证成功处理
    loginFilter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
      @Override
      public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        Map<String, Object> result = new HashMap<>();
        result.put("msg", "登录失败:" + exception.getMessage());
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=UTF-8");
        String s = new ObjectMapper().writeValueAsString(result);
        response.getWriter().println(s);
      }
    }); // 认证失败后的处理
    return loginFilter;
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated() // 所有请求都必须认证
            .and()
            .formLogin()
            .and()
            .exceptionHandling()
            .authenticationEntryPoint(new AuthenticationEntryPoint() {
              @Override
              public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                response.setStatus(HttpStatus.UNAUTHORIZED.value());  // 401
                response.getWriter().println("请认证之后再去处理!");
              }
            }) // 认证异常
            .and()
            .logout()
//            .logoutUrl("/logout")
            // 配置多个注销请求
            .logoutRequestMatcher(new OrRequestMatcher(
                    new AntPathRequestMatcher("/logout", HttpMethod.DELETE.name()),
                    new AntPathRequestMatcher("/logout", HttpMethod.GET.name())
            ))
            .logoutSuccessHandler((req, resp, auth) -> {
              Map<String, Object> result = new HashMap<>();
              result.put("msg", "注销成功");
              result.put("用户信息", auth.getPrincipal());
              resp.setStatus(HttpStatus.OK.value());
              resp.setContentType("application/json;charset=UTF-8");
              String s = new ObjectMapper().writeValueAsString(result);
              resp.getWriter().println(s);
            })
            .and()
            .csrf().disable() // 关闭csrf保护

    ;

    // at：用来某个 filter 替换过滤器中哪个 filter
    // before: 放在过滤器中哪个 filter 之前
    // after: 放在过滤器中哪个 filter 之后
    http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
  }
}
